function DayDetailController(calendarViewBody, dayDetailDialog, handIcon, repository) {
    this._calendarViewBody = calendarViewBody;
    this._dayDetailDialog = dayDetailDialog;
    this._handIcon = handIcon;
    this._repository = repository;
}

DayDetailController.prototype = {
    register: function(calendar) {
        var me = this;
        this._calendar = calendar;
        this._dayDetailDialog.createDialog();

        this._registerDayDetailDialogEvents();

        this._registerNoteIconEvents();
        this._calendarViewBody.onDraw(function(days) {
            me._registerNoteIconEvents(days);
        });

        this._calendarViewBody.setDefaultMode(new DayViewMode(calendar, this._dayDetailDialog));
        this._calendarViewBody.restoreDefaultMode();

        this._handIcon.onClick(function() {
            me._handIcon.activate();
            me._calendarViewBody.restoreDefaultMode();
        });
    },

    _registerDayDetailDialogEvents: function() {
        var me = this;
        this._dayDetailDialog.onApply(function() {
            var hasChange = me._dayDetailDialog.hasChanged();
            if (hasChange) {
                me._applyDay();
            } else {
                me._dayDetailDialog.closeDialog();
            }
        });
    },

    _applyDay: function() {
        var travelAndError = this._dayDetailDialog.toTravel();

        var travel = travelAndError[0];
        var dateError = travelAndError[1];

        var dayDetail = this._dayDetailDialog.toDayDetail();
        var error = this._getDialogError(travel, dayDetail, dateError);

        if (error.hasError()) {
            this._dayDetailDialog.showErrors(error);
            return;
        }

        if (travel) {
            var travelAction = travel.createAction();
            var travelConflicts = this._calendar.hasConflict(travelAction);
            if (travelConflicts.hasConflict()) {
                this._dayDetailDialog.showConflictErrorMessages(travelConflicts.conflictMessages);
                return;
            }

            this._calendar.applyAction(travelAction);
        }

        this._calendar.applyAction(dayDetail.createAction());

        this._dayDetailDialog.closeDialog();
    },

    _getDialogError: function(travel, dayDetail, dateError) {
        var error = new Error();

        var travelError = new Error();
        if (travel && !travel.isValid()) {
            travelError.join(travel.getError());
        }

        travelError.errors.foreach(function(errorItem) {
            var item = errorItem;
            dateError.errors.foreach(function(dateErrorItem) {
                if (errorItem.errorField == dateErrorItem.errorField && errorItem.errorMessage == i18n("MSG_TRAVEL_DATE_REQUIRED")) {
                    item = dateErrorItem;
                }
            });
            error.errors.push(item);
        });

        if (!dayDetail.isValid()) {
            error.join(dayDetail.getError());
        }

        return error;
    },

    _registerNoteIconEvents: function(days) {
        var me = this;
        this._getDayElements(days).foreach(function(dayElement) {
            var date = dayElement.getDate();
            dayElement.initNote(
                    function(note) { me._saveNoteSuccessCallback(note, date); },
                    function(note) { me._noteDeletedFromPaletteCallback(note); },
                    function() { return me._getNoteFromCache(date); },
                    function() { me._calendarViewBody.restoreDefaultMode(); }
                );
        });

    },

    _getNoteFromCache: function(date) {
        return this._repository.getNoteByDate(date);
    },

    _noteDeletedFromPaletteCallback: function(deletedNote) {
        var me = this;
        var calendarNote = new CalendarNote({ id: '', content: '', creator: '', date: deletedNote.properties.date });
        me._updateNoteCache(calendarNote);
        me._updateNoteView();
    },

    _saveNoteSuccessCallback: function(note, date) {
        var me = this;
        var calendarNote = new CalendarNote({ id: note.id, content: note.content, creator: note.creator, date: note.properties.date });
        me._updateNoteCache(calendarNote);
        me._updateNoteView(date);
    },

    _updateNoteCache: function(note) {
        this._repository.updateNoteCache(note);
    },

    _updateNoteView: function(date) {
        var day = this._repository.getMonthByYearMonth(date.toYearMonth()).getDay(date);
        this._calendarViewBody.updateDays([day]);        
    },

    _getDayElements: function(days) {
        if (days) {
            return days.select(function(day) {
                var domId = DayView.getDateCellId(day.getDate());
                return new DayElement($('#' + domId));
            });
        }

        return $.map($('.current-month-day .day'), function(dayDom) {
            return new DayElement($(dayDom));
        });
    }
};

function DayViewMode(calendar, dayDetailDialog) {
    this._calendar = calendar;
    this._dayDetailDialog = dayDetailDialog;
    this.cursor = 'calendar-default-cursor';
    this._showNoteIcontimer = null;

}

DayViewMode.prototype = {
    onMouseover: function(sender) {
        clearTimeout(this._showNoteIcontimer);
        this._showNoteIcontimer = setTimeout(function() {
            $(sender).addClass("day-hover");
        }, 330);
    },
    onMouseout: function(sender) {
        clearTimeout(this._showNoteIcontimer);
        var dom = $(sender);
        dom.removeClass("day-hover");
    },
    _getXPosition: function(dom, x) {
        return (x - dom.offset().left) / dom.width();
    },

    onSelection: function(sender) {
        //currently on support one day
        var dom = $(sender);
        var date = new DayElement(dom).getDate();

        this._calendar.beginTransaction();
        var dayModel = this._calendar.getDay(date);

        if (!dayModel) {
            return;
        }

        var lastUsedLocations = this._calendar.getLastUsedLocations(dayModel.getDate());
        this._dayDetailDialog.showDialog(dayModel, lastUsedLocations, [dom.offset().left, dom.offset().top]);        
    }
};
